﻿using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using MugenInjection.Web.Interface;

namespace MugenInjection.Wcf.Scope
{
    /// <summary>
    /// Represents the scope provider for <see cref="OperationContext"/>.
    /// </summary>
    public sealed class ContextRequestScopeProvider : IDispatchMessageInspector, IScopeProvider
    {
        #region Implementation of IDispatchMessageInspector

        /// <summary>
        /// Called after an inbound message has been received but before the message is dispatched to the intended operation.
        /// </summary>
        /// <returns>
        /// The object used to correlate state. This object is passed back in the <see cref="M:System.ServiceModel.Dispatcher.IDispatchMessageInspector.BeforeSendReply(System.ServiceModel.Channels.Message@,System.Object)"/> method.
        /// </returns>
        /// <param name="request">The request message.</param><param name="channel">The incoming channel.</param><param name="instanceContext">The current service instance.</param>
        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            return null;
        }

        /// <summary>
        /// Called after the operation has returned but before the reply message is sent.
        /// </summary>
        /// <param name="reply">The reply message. This value is null if the operation is one way.</param><param name="correlationState">The correlation object returned from the <see cref="M:System.ServiceModel.Dispatcher.IDispatchMessageInspector.AfterReceiveRequest(System.ServiceModel.Channels.Message@,System.ServiceModel.IClientChannel,System.ServiceModel.InstanceContext)"/> method.</param>
        public void BeforeSendReply(ref Message reply, object correlationState)
        {
            OnReleaseScope(GetCurrentScope());
        }

        #endregion

        #region Implementation of IScopeProvider

        /// <summary>
        /// Occured when need dispose scope.
        /// </summary>
        public event Action<object> ReleaseScope;

        /// <summary>
        /// Gets the current scope.
        /// </summary>
        /// <returns></returns>
        public object GetCurrentScope()
        {
            OperationContext operationContext = OperationContext.Current;
            if (operationContext == null)
                throw new InvalidOperationException(
                    "There is no current OperationContext, ContextRequestScopeProvider can't access to the current session.");
            return operationContext;
        }

        #endregion

        #region Events

        private void OnReleaseScope(object obj)
        {
            Action<object> handler = ReleaseScope;
            if (handler != null)
                handler(obj);
        }

        #endregion
    }
}